package main

import (
	"context"
	"fmt"
	"io"
	"os"
	"time"

	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/container"
	"github.com/docker/docker/api/types/filters"
	"github.com/docker/docker/client"
	"github.com/docker/go-connections/nat"
)

func main() {
	/* // 使用golang的Docker SDK,通过ssh远程访问docker守护进程
	helper, err := connhelper.GetConnectionHelper("ssh://wenzu@192.168.1.2")
	if err != nil {
		panic(err)
	}
	httpClient := &http.Client{
		// No tls
		// No proxy
		Transport: &http.Transport{
			DialContext: helper.Dialer,
		},
	}
	cli, err := client.NewClientWithOpts(client.FromEnv,
		client.WithHTTPClient(httpClient),
		client.WithHost(helper.Host),
		client.WithDialContext(helper.Dialer),
	)
	if err != nil {
		panic(err)
	}
	cli.NegotiateAPIVersion(context.Background()) // 该函数可以协商API版本并解决版本不匹配问题
	*/
	
	cli, err := client.NewClientWithOpts(client.FromEnv) // 连接本地的docker
	if err != nil {
		panic(err)
	}

	imageName := "mongo:5.0"

	filters := filters.NewArgs(filters.Arg("reference", imageName))
	images, err := cli.ImageList(context.Background(), types.ImageListOptions{Filters: filters})
	if err != nil {
		panic(err)
	}
	if len(images) == 0 {
		// ImagePull 请求 docker 主机从远程注册表中提取图像。
		// 如果操作未经授权，它会执行特权功能并再试一次。由调用者来处理 io.ReadCloser 并正确关闭它。
		out, err := cli.ImagePull(context.Background(), imageName, types.ImagePullOptions{})
		if err != nil {
			panic(err)
		}
		defer out.Close()
		// os.Stdout是指向标准输出描述符的打开文件。
		io.Copy(os.Stdout, out)
	}

	ctx := context.Background()
	resp, err := cli.ContainerCreate(ctx,
		// 容器的配置数据。只保存容器的可移植配置信息。
		// “可移植”意味着“独立于我们运行的主机”(容器内部的配置)。
		&container.Config{
			Image: imageName, // 对应docker镜像的名字
			ExposedPorts: nat.PortSet{ // 要暴露端口列表
				"27017/tcp": {},
			},
		},

		// 主机的配置。保存不可移植的配置信息。
		&container.HostConfig{
			// 暴露端口(容器)和主机之间的端口映射
			PortBindings: nat.PortMap{
				"27017/tcp": []nat.PortBinding{
					{
						HostIP:   "0.0.0.0", // 主机IP地址
						HostPort: "0",       // 主机端口号。0代表随机端口
					},
				},
			},
		},
		nil, nil, "",
	)
	if err != nil {
		panic(err)
	}

	// ContainerStart 根据containerID,向 docker 守护进程发送请求以启动容器。
	err = cli.ContainerStart(ctx, resp.ID, types.ContainerStartOptions{})
	if err != nil {
		panic(err)
	}

	// ContainerInspect 返回容器信息。
	inspRes, err := cli.ContainerInspect(ctx, resp.ID)
	if err != nil {
		panic(err)
	}

	// Ports是PortBinding的集合
	fmt.Printf("listening at %+v\n", inspRes.NetworkSettings.Ports["27017/tcp"][0])
	fmt.Printf("listening at %+v\n", inspRes)
	fmt.Println("container started")
	fmt.Println(inspRes.Mounts[0].Name)

	time.Sleep(40 * time.Second)

	// ContainerRemove 根据containerID,杀死并从 docker 主机中删除一个容器。
	err = cli.ContainerRemove(ctx, resp.ID, types.ContainerRemoveOptions{
		Force: true,
	})
	if err != nil {
		panic(err)
	}
	for _, res := range inspRes.Mounts{
		err = cli.VolumeRemove(ctx, res.Name, true)
		if err != nil {
			panic(err)
		}
	}

}
